package 链表

/*
146. LRU 缓存
请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类：
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中，则返回关键字的值，否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在，则变更其数据值 value ；如果不存在，则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ，则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例：
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废，缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废，缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

思路:
一旦出现键值对, 就要想到哈希表,
本题难点在于维护访问以及插入时间的先后顺序, 首先先到栈,队列,链表
但题目要求每个get,put更新数据之后, 该数据需要被设置为最新访问的数据
这就意味着:
	- 1.元素能被随机访问
	  2.需要把该数据插入头部或尾部
链表可以快速实现插入尾部, 但是如何实现随机访问呢?
使用哈希表, 如果哈希表的值包含链表的位置信息, 那么可以在O(1)时间内访问链表元素
最后, 由于链表记录了访问的时间顺序, 所以链表中的元素必须存储键,
这样才能通过键找到哈希表的内容, 进而实现删除要求

例如: 容量为2的链表  进行如下操作
cache.put(1,1)
cache.put(2,2)
cache.get(1)  //返回1
cache.put(3,3) //该操作会使得密钥2作废

双链表的tail方向是最后使用的元素
*/

type LinkNode struct {
	key, value int       //存储的key,value
	prev, next *LinkNode //前,后节点
}

func initLinkNode(key, value int) *LinkNode { // 初始化
	return &LinkNode{
		key:   key,
		value: value,
	}
}

type LRUCache struct {
	size       int
	capacity   int
	cache      map[int]*LinkNode //哈希表
	head, tail *LinkNode         //链表
}

func Constructor(capacity int) LRUCache { // 构造函数
	l := LRUCache{ //创建缓存结构体
		cache:    map[int]*LinkNode{}, //声明哈希表
		head:     initLinkNode(0, 0),  //声明头节点
		tail:     initLinkNode(0, 0),  //声明尾节点
		capacity: capacity,            //设置缓存的大小
	}
	l.head.next = l.tail //头结点指向为节点
	l.tail.prev = l.head //尾节点指向头节点

	return l
}
func (this *LRUCache) Get(key int) int { // 获取元素
	if _, ok := this.cache[key]; !ok { //如果根据key在map中找不到,直接返回-1即可
		return -1
	}

	node := this.cache[key] //能找到,获取node节点
	this.moveToHead(node)   //因为元素被访问了, 是最新被访问的数据, 需要将当前节点放到头部
	return node.value
}
func (this *LRUCache) moveToHead(node *LinkNode) {
	this.removeNode(node) //把当前节点从链表剔除
	this.addToHead(node)  //把当前链表加入头节点的下一个节点
}
func (this *LRUCache) removeNode(node *LinkNode) { //把当前节点从当前链表剔除
	node.prev.next = node.next
	node.next.prev = node.prev
}
func (this *LRUCache) addToHead(node *LinkNode) {
	node.prev = this.head      //当前节点的前节点为head
	node.next = this.head.next //当前节点的后节点为head.next
	this.head.next.prev = node //
	this.head.next = node
}

// 存放节点
func (this *LRUCache) Put(key int, value int) {
	if _, ok := this.cache[key]; !ok { //如果在map中不存在
		node := initLinkNode(key, value) //创建新节点
		this.cache[key] = node           //加入哈希表
		this.addToHead(node)             //放入头部
		this.size++                      //缓存大小++
		if this.size > this.capacity {   //如果当前缓存大小已经越界, 删除尾部元素, map删除尾部元素
			removed := this.removeTail()
			delete(this.cache, removed.key)
			this.size-- //容量--
		}
	} else {
		node := this.cache[key] //如果存在该key,直接进行更新即可
		node.value = value
		this.moveToHead(node)
	}
}

func (this *LRUCache) removeTail() *LinkNode {
	node := this.tail.prev
	this.removeNode(node)
	return node
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */
